package analisador;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import util.TabelaModel;

public class LexicoModel implements Serializable {

	private static final long serialVersionUID = 1L;

	private Map<Integer, Map<Character, Integer>> analisador; // M[0][c-1]
	private List<String> proxLetras;
	private TabelaModel tabelaModel;
	private static int proxEstado = 1;

	public static Integer estadoValidoAtual = 0;
	public static String proxLetraValida = "";

	public Map<Integer, Map<Character, Integer>> getAnalisador() {
		if (this.analisador == null) {
			this.analisador = new HashMap<Integer, Map<Character, Integer>>();
		}
		return analisador;
	}

	public void setAnalisador(Map<Integer, Map<Character, Integer>> analisador) {
		this.analisador = analisador;
	}

	public static Integer getEstadoValidoAtual() {
		return estadoValidoAtual;
	}

	public static void setEstadoValidoAtual(Integer estadoValidoAtual) {
		LexicoModel.estadoValidoAtual = estadoValidoAtual;
	}

	public static String getProxLetraValida() {
		return proxLetraValida;
	}

	public static void setProxLetraValida(String proxLetraValida) {
		LexicoModel.proxLetraValida = proxLetraValida;
	}

	public List<String> getProxLetras() {
		if (this.proxLetras == null) {
			this.proxLetras = new ArrayList<String>();
		}
		return proxLetras;
	}

	public void setProxLetras(List<String> proxLetras) {
		this.proxLetras = proxLetras;
	}

	public void inserePalavra(String palavra) {
		int estado = 1;

		for (int i = 0; i < palavra.length(); i++) {
			char letra = palavra.charAt(i);
			
			// temp map
			Map<Character, Integer> map = getAnalisador().get(estado);
			if (map == null) {
				map = new HashMap<Character, Integer>();
			}

			// se o estado ja tem a letra, pega o estado para qual a letra vai
			if (getAnalisador().get(estado) != null && getAnalisador().get(estado).containsKey(letra)) {
				estado = getAnalisador().get(estado).get(letra);
			} else {
				map.put(letra, ++proxEstado);
				getAnalisador().put(estado, map);
				estado = proxEstado;
			}

			// estado final
			if (i == palavra.length() - 1) {
				if (getAnalisador().containsKey(estado)) {
					map = getAnalisador().get(estado);
					getAnalisador().put(estado, getEstadoFinal(map));
				} else {
					getAnalisador().put(estado, getEstadoFinal());
				}
			}
		}
		//mostraMapa(analisador);
	}

	public boolean verificaPalavra(String palavra) {
		boolean valida = false;
		int nextValido = 1;

		for (int i = 0; i < palavra.length(); i++) {
			char letra = palavra.charAt(i);

			if (Character.isLetter(letra)) {
				if(getAnalisador().get(nextValido) == null) {
					break;
				}
				// o proximo estado nao contem a letra informada
				if (!getAnalisador().get(nextValido).containsKey(letra)) {
					break;
				} else {
					nextValido = getAnalisador().get(nextValido).get(letra);
					// para mostrar o estado atual na tabela
					setEstadoValidoAtual(nextValido);
				}
				if (i == palavra.length() - 1) {
					if (getAnalisador().get(nextValido).containsKey('*')) {
						valida = true;
					}
				}
			}
		}
		return valida;
	}

	private Map<Character, Integer> getEstadoFinal() {
		Map<Character, Integer> map = new HashMap<Character, Integer>();
		map.put('*', 0);
		return map;
	}
	
	private Map<Character, Integer> getEstadoFinal(Map<Character, Integer> map) {
		map.put('*', 0);
		return map;
	}

	private void mostraMapa(Map<Integer, Map<Character, Integer>> analisador) {
		Set<Entry<Integer, Map<Character, Integer>>> list = analisador.entrySet();
		for (Entry<Integer, Map<Character, Integer>> estado : list) {
			System.out.print(estado.getKey() + " - ");
			System.out.println(estado.getValue());
		}
	}

	public List<String> getLinhas() {
		ArrayList<String> linhas = new ArrayList<String>();
		Set<Entry<Integer, Map<Character, Integer>>> entrySet = getAnalisador().entrySet();
		for (Entry<Integer, Map<Character, Integer>> entry : entrySet) {
			Integer key = entry.getKey();
			if (entry.getValue().containsKey('*')) {
				linhas.add(key + "*");
			} else {
				linhas.add(key.toString());
			}
		}
		return linhas;
	}

	public List<String> getColunas(Integer indice) {
		tabelaModel = new TabelaModel();
		ArrayList<String> colunas = tabelaModel.getLinhaVazia();

		if (getAnalisador().containsKey(indice)) {
			Map<Character, Integer> colunasMap = getAnalisador().get(indice);
			Set<Entry<Character, Integer>> entrySet = colunasMap.entrySet();
			for (Entry<Character, Integer> entry : entrySet) {
				Character letra = entry.getKey();
				if (!letra.equals('*')) {
					// colunas = tabelaModel.getLinhaEstadoFinal();
					int pos = tabelaModel.getIndiceLetra(letra);
					colunas.set(pos, entry.getValue().toString());
				}
			}
		}
		return colunas;
	}

	public void limpaAutomato() {
		getAnalisador().clear();
		proxEstado = 1;
	}

	public Integer getEstadoAtual(String palavra) {
		int nextValido = 1;
		int estado = 0;

		for (int i = 0; i < palavra.length(); i++) {
			char letra = palavra.charAt(i);

			if (Character.isLetter(letra)) {
				if (!getAnalisador().get(nextValido).containsKey(letra)) {
					setProxLetras(getListLetras(1));
					return 0;
				} else {
					estado = nextValido;
					nextValido = getAnalisador().get(nextValido).get(letra);
					// setProxLetras(getListLetras(nextValido));
				}
			} else {
				if (letra == ' ') {
					estado = nextValido;
				}
			}
		}
		return estado;
	}

	public List<String> getListLetras(int indice) {
		List<String> proxLetras = new ArrayList<String>();

		Map<Character, Integer> map = getAnalisador().get(indice);
		Set<Entry<Character, Integer>> entrySet = map.entrySet();
		for (Entry<Character, Integer> entry : entrySet) {
			proxLetras.add(entry.getKey().toString());
		}
		return proxLetras;
	}
}
